import {computed, ComputedRef} from 'vue';
import dayjs from 'dayjs';

/**
 * 示例方法：将数组去重
 * @param array 需要去重的数组
 * @returns 去重后的数组
 */
export function devUniqueArray<T>(array: T[]): T[] {
    return Array.from(new Set(array));
}

/**
 * 深拷贝对象（支持循环引用和特殊数据类型）
 * @param obj 需要拷贝的对象
 * @param map WeakMap 用于记录已拷贝对象，防止循环引用
 * @returns 深拷贝后的对象
 */
export function devDeepClone<T>(obj: T, map = new WeakMap()): T {
    // 检查 null 和非对象类型
    if (obj === null || typeof obj !== 'object') return obj;

    // 处理循环引用
    if (map.has(obj)) return map.get(obj) as T;

    // 处理 Date 和 RegExp
    if (obj instanceof Date) return new Date(obj.getTime()) as T;
    if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags) as T;

    // 处理 Set
    if (obj instanceof Set) {
        return new Set([...obj].map(v => devDeepClone(v, map))) as T;
    }

    // 处理 Map
    if (obj instanceof Map) {
        return new Map(
            [...obj.entries()].map(([k, v]) => [devDeepClone(k, map), devDeepClone(v, map)])
        ) as T;
    }

    // 处理普通对象和数组
    let result: any;
    if (Array.isArray(obj)) {
        result = [];
    } else {
        result = Object.create(Object.getPrototypeOf(obj)); // 保留原型链
    }

    // 记录当前对象
    map.set(obj, result);

    // 递归拷贝对象的每个属性
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            result[key] = devDeepClone((obj as any)[key], map);
        }
    }

    return result;
}

/**
 * 示例方法：格式化日期
 * @param date 需要格式化的日期对象
 * @param format 格式字符串，默认为 'YYYY-MM-DD'
 * @returns 格式化后的日期字符串
 */
export function devFormatDate(date: Date, format: string = 'YYYY-MM-DD'): string {
    const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: '2-digit', day: '2-digit' };
    return new Intl.DateTimeFormat('default', options).format(date);
}

/**
 * 示例方法：检查字符串是否为有效的邮箱格式
 * @param email 需要检查的邮箱字符串
 * @returns 如果是有效的邮箱格式，返回 true；否则返回 false
 */
export function devIsValidEmail(email: string): boolean {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
}

/**
 * 生成唯一值
 */
export function devGenerateUniqueKey(): string {
    return 'devecoui' + Math.random().toString(36).substr(2);
}


/**
 * 类型过滤函数
 */
export function devFilterMethods<T extends object>(source: object, targetType: T): T {
    const result = {} as T;
    for (const key in source) {
        if (key in targetType && typeof (source as any)[key] === 'function') {
            (result as any)[key] = (source as any)[key];
        }
    }
    return result;
}

/**
 * devGetFilteredProps 函数用于过滤props中的方法。
 * @param props - props对象。
 * @param keysToExclude - 需要过滤的方法名称。
 * @returns 返回过滤后的props对象。
 */
export function devGetFilteredProps<T extends Record<string, any>>(props: T, keysToExclude: string[]) {
    const filteredProps = { ...props };
    keysToExclude.forEach(key => {
        delete filteredProps[key];
    });
    return filteredProps;
}

/**
 * 比较两个数组是否相等
 * @param arr1 - 第一个数组
 * @param arr2 - 第二个数组
 * @returns 如果两个数组相等则返回 true，否则返回 false
 */
export function devArraysEqual<T>(arr1: T[], arr2: T[]): boolean {
    // 检查是否两个输入均为数组类型
    if (!Array.isArray(arr1) || !Array.isArray(arr2)){
        console.warn('devArraysEqual:对比参数中含有非数组数据')
        return false;
    }
    // 首先检查两个数组的长度是否相等
    if (arr1.length !== arr2.length) return false; // 长度不相等，直接返回 false

    // 使用 every 方法检查每个元素是否相等
    return arr1.every((value, index) => value === arr2[index]); // 检查每个元素
}

/**
 * 判断对象是否为空
 * @param obj - 需要判断的对象
 * @returns 如果对象为空则返回 true，否则返回 false
 */
export function devHasParams(obj: Record<string, any>): boolean {
    return Object.keys(obj).length > 0;
}

/**
 * 判断一个数组（外部数组）中是否包含目标数组或目标对象
 *
 * @param outerArray - 包含嵌套数组或对象的外部数组
 * @param target - 需要判断是否存在的目标数组或目标对象
 * @returns 如果目标数组或目标对象存在于外部数组中，返回 true，否则返回 false
 */
export function devIsItemInArray(outerArray: (any[] | Record<string, any>)[], target: any[] | Record<string, any>): boolean {
    // 使用 `some` 方法遍历 outerArray 的每一项
    return outerArray.some((item) => {
        // 将每一项 `item` 转换为字符串，比较它与目标项是否相等
        return JSON.stringify(item) === JSON.stringify(target);
    });
}


/**
 * 递归合并两个对象，生成新的对象，且不会修改原始对象，
 * 并根据特定层次或全局的过滤规则过滤指定的属性。
 * 同时，当 obj2 的属性值为 undefined 或 null 时，跳过合并该属性。
 * 过滤逻辑同时作用于 obj1 和 obj2 中的属性。
 *
 * @param obj1 - 第一个对象，作为合并基准对象
 * @param obj2 - 第二个对象，用于覆盖第一个对象的值
 * @param filterRules - 可选的过滤规则对象，可以指定某一层要过滤的属性，也可以通过 "_all" 全局过滤所有层级的特定属性
 * @returns 返回合并后的新对象
 */
export function devDeepMerge<T extends Record<string, any>, U extends Record<string, any>>(
    obj1: T,
    obj2: U,
    filterRules: Record<string, any> = {}
): T & U {
    const result = { ...obj1 }; // 复制 obj1，创建新对象，避免修改原始数据

    for (const key in obj2) {
        if (obj2.hasOwnProperty(key)) {
            const value = obj2[key];

            // 跳过 undefined 和 null 的值
            if (value === undefined || value === null) {
                continue;
            }

            // 处理全局过滤：检查是否需要在所有层级中过滤某些字段
            if (filterRules["_all"] && filterRules["_all"].includes(key)) {
                continue; // 跳过全局过滤的字段
            }

            // 处理局部层级过滤：检查当前层级是否有指定的过滤规则
            if (filterRules.hasOwnProperty(key)) {
                if (filterRules[key] === true) {
                    continue; // 跳过局部过滤的字段
                }
                if (devIsObject(filterRules[key]) && devIsObject(value) && devIsObject(obj1[key])) {
                    result[key] = devDeepMerge(obj1[key], value, filterRules[key]) as any;
                    continue;
                }
            }

            // 如果两个都是对象，递归合并
            if (devIsObject(obj1[key]) && devIsObject(value)) {
                result[key] = devDeepMerge(obj1[key], value, filterRules[key] || {}) as any;
            } else {
                // 直接覆盖 result 的值，而不是 obj1
                result[key] = value;
            }
        }
    }

    // 检查并处理 obj1 中的过滤字段
    for (const key in obj1) {
        if (obj1.hasOwnProperty(key)) {
            // 处理全局过滤：检查是否需要在所有层级中过滤某些字段
            if (filterRules["_all"] && filterRules["_all"].includes(key)) {
                delete result[key]; // 从结果中删除全局过滤的字段
            }

            // 处理局部层级过滤：检查当前层级是否有指定的过滤规则
            if (filterRules.hasOwnProperty(key)) {
                if (filterRules[key] === true) {
                    delete result[key]; // 从结果中删除局部过滤的字段
                }
            }
        }
    }

    return result as T & U;
}


/**
 * 判断值是否为对象，可选是否判断对象是否有值
 * @param value - 需要判断的值
 * @param checkHasValue - 是否检查对象是否含有键值（默认 false）
 * @returns 如果是对象（且可选地有值）则返回 true，否则返回 false
 */
export function devIsObject(value: any, checkHasValue = false): value is Record<string, any> {
    const isObj = value !== null && typeof value === 'object' && !Array.isArray(value);
    return checkHasValue ? isObj && Object.keys(value).length > 0 : isObj;
}

/**
 * @description 深度比较两个值是否相等，支持所有数据类型。
 * @param value1 - 需要比较的第一个值
 * @param value2 - 需要比较的第二个值
 * @returns 返回布尔值，表示两个值是否相等
 */
export function devDeepEqual(value1: any, value2: any): boolean {
    // 如果两个值是严格相等的，直接返回 true
    if (Object.is(value1, value2)) {
        return true;
    }

    // 如果两个值类型不同，直接返回 false
    if (typeof value1 !== typeof value2) {
        return false;
    }

    // 特殊情况处理：如果是 null 和 undefined，类型相同的情况下已经排除 Object.is 的情况
    if (value1 === null || value2 === null || value1 === undefined || value2 === undefined) {
        return false;
    }

    // 如果是日期对象，比较它们的时间戳
    if (value1 instanceof Date && value2 instanceof Date) {
        return value1.getTime() === value2.getTime();
    }

    // 如果是正则表达式对象，比较它们的字符串表示
    if (value1 instanceof RegExp && value2 instanceof RegExp) {
        return value1.toString() === value2.toString();
    }

    // 如果是数组，递归比较每个元素
    if (Array.isArray(value1) && Array.isArray(value2)) {
        if (value1.length !== value2.length) {
            return false;
        }
        for (let i = 0; i < value1.length; i++) {
            if (!devDeepEqual(value1[i], value2[i])) {
                return false;
            }
        }
        return true;
    }

    // 如果是对象，比较它们的键和值
    if (typeof value1 === 'object' && typeof value2 === 'object') {
        const keys1 = Object.keys(value1);
        const keys2 = Object.keys(value2);

        // 如果两个对象的键数量不同，直接返回 false
        if (keys1.length !== keys2.length) {
            return false;
        }

        // 比较每个键对应的值
        for (const key of keys1) {
            if (!devDeepEqual(value1[key], value2[key])) {
                return false;
            }
        }
        return true;
    }

    // 其他情况，返回 false（包括函数、Symbol 等类型）
    return false;
}

/**
 * 获取两个数组的差集
 * @param array1 - 第一个数组
 * @param array2 - 第二个数组
 * @returns 返回 array1 与 array2 的双向差集
 */
export function devGetArrayDifference<T>(array1: T[], array2: T[]): T[] {
    // 从 array1 中找出不在 array2 中的元素
    const diff1 = array1.filter(item => !array2.includes(item));

    // 从 array2 中找出不在 array1 中的元素
    const diff2 = array2.filter(item => !array1.includes(item));

    // 合并两边的差集并返回
    return [...diff1, ...diff2];
}

/**
 * 解析json字符串解析失败时返回默认值
 * @param str 要解析的JSON字符串
 * @param defaultJson 解析失败时返回的默认对象
 * @returns 解析后的对象或默认对象
 */
export function devParseJson<T>(str: string, defaultJson: T = {} as T): T {
    if (!str) {
        return defaultJson;  // 空字符串、null或undefined都返回默认对象
    }
    try {
        return JSON.parse(str) as T;  // 强制转换为泛型T
    } catch (error) {
        console.error(`解析JSON字符串失败:`, error);
        return defaultJson;  // 解析失败返回默认对象
    }
}

/**
 * 和并数组中的所有元素并且去重
 * @param arrays
 * @param defaultValue
 */
export function devMergeArrays<T>(arrays: (T[] | any)[], defaultValue: T[] = []): T[] {
    try {
        // 合并数组，过滤非数组项
        const resultSet = new Set<T>();

        // 遍历每个数组
        arrays.forEach((array) => {
            if (Array.isArray(array)) {
                array.forEach(item => {
                    resultSet.add(item); // 将元素添加到 Set 中，自动去重
                });
            }
        });

        // 将 Set 转换为数组并返回
        return Array.from(resultSet);
    } catch (error) {
        // 发生错误时返回默认值
        console.error('Error during array merging:', error);
        return defaultValue;
    }
}


type ExtractKeyType<T, Key extends string | string[]> =
    Key extends string ? T[Key & keyof T] :
        Key extends [infer K1, ...infer Rest] ?
            K1 extends string ?
                Rest extends string[] ?
                    ExtractKeyType<T[K1 & keyof T], Rest> :
                    never :
                never :
            never;

/**
 * 处理 key 为字符串的情况
 * 从对象数组中提取指定属性值，并返回一个计算属性
 * @param list - 对象数组
 * @param key - 需要提取的属性名
 * @param multiple - 是否多选
 * @returns 包含提取属性值的计算属性
 */
export function devUseExtractSelected<T extends Record<string, any>, Key extends string>(
    list?: T[],
    key?: Key,
    multiple?: boolean
): ComputedRef<ExtractKeyType<T, Key>[]>;

/**
 * 处理 key 为字符串数组（嵌套路径）的情况
 * 从对象数组中提取指定属性值，并返回一个计算属性
 * @param list - 对象数组
 * @param key - 需要提取的属性名
 * @param multiple - 是否多选
 * @returns 包含提取属性值的计算属性
 */
export function devUseExtractSelected<T extends Record<string, any>, Key extends string[]>(
    list?: T[],
    key?: Key,
    multiple?: boolean
): ComputedRef<ExtractKeyType<T, Key>[]>;

/**
 * 实现函数
 */
export function devUseExtractSelected<T extends Record<string, any>>(
    list: T[] = [],
    key: string | string[] = 'value',
    multiple = false
) {
    // 辅助函数：递归获取嵌套属性值
    const getNestedValue = (obj: T, keys: string[]): any => {
        return keys.reduce((acc, currentKey) => acc?.[currentKey], obj);
    };

    return computed(() => {
        const resolvedKey = Array.isArray(key) ? key : [key];

        if (multiple) {
            return list.reduce<any[]>((arr, item) => {
                const value = getNestedValue(item, resolvedKey);
                if (Array.isArray(value)) {
                    arr.push(...value);
                }
                return arr;
            }, []);
        } else {
            return list.map(item => getNestedValue(item, resolvedKey)).filter(Boolean);
        }
    });
}

/**
 * 清空数组并替换为新数据
 * @param arr - 需要更新的数组
 * @param newData - 新的数据
 */
export function devReplaceArrayData<T>(arr: T[], newData: T[]): void {
    // 清空原数组（移除所有元素）
    arr.splice(0, arr.length);

    // 将新的数据逐个推入原数组
    arr.push(...newData);
}

/**
 * 判断一个值是否为真实数字（数字类型或可转换的字符串数字）
 * @param value 任意值
 * @returns 是否为合法数字
 */
export function devIsRealNumber(value: unknown): boolean {
    return (
        (typeof value === 'number' && !isNaN(value)) ||
        (typeof value === 'string' && value.trim() !== '' && !isNaN(Number(value)))
    );
}

/**
 * 将手机号中间四位替换为 *
 * @param phone - 11 位手机号字符串
 * @returns 替换后的手机号或原始输入
 */
export const devMaskPhoneNumber = (phone: string): string => {
    if (!/^\d{11}$/.test(phone)) {
        console.error('请输入有效的11位手机号');
        return phone;
    }
    return phone.slice(0, 3) + '****' + phone.slice(7);
};

/**
 * 传入生日字符串，计算年龄
 * @param birthdayStr - 生日字符串，格式建议为 'YYYY-MM-DD'
 * @returns 年龄（number 类型）
 */
export const devCalculateAge = (birthdayStr: string): number => {
    const birthday = dayjs(birthdayStr);
    const today = dayjs();

    const age = today.diff(birthday, 'year');
    return age;
};

/**
 * 从 URL 中提取文件名，可选是否保留扩展名
 * @param url 文件地址
 * @param withExtension 是否保留扩展名（true 保留，false 不保留），默认 false
 * @returns 提取到的文件名
 */
export function devGetFileNameFromUrl(url: string, withExtension: boolean = false): string {
    try {
        const cleanUrl = url.split('?')[0]; // 去掉 query 参数
        const segments = cleanUrl.split('/');
        const fileName = segments[segments.length - 1];

        if (withExtension) return fileName;

        const dotIndex = fileName.lastIndexOf('.');
        if (dotIndex === -1) return fileName;
        return fileName.substring(0, dotIndex);
    } catch {
        return '未命名文件';
    }
}


/**
 * 判断给定文件名的类型是 'image' 还是 'file'
 *
 * @param filename - 文件名或路径，例如 'example.jpg' 或 '/uploads/abc/IMG_1234.PNG'
 * @returns 'image' 表示是图片文件，否则返回 'file'
 */
export function devGetFileTypeByExtension(filename: string): 'file' | 'image' {
    // 定义常见图片扩展名（小写）
    const imageExtensions = [
        '.jpg', '.jpeg', '.png', '.gif', '.bmp',
        '.webp', '.svg', '.tiff', '.ico', '.avif'
    ];

    // 提取文件名最后的后缀（包括 .），统一转为小写
    const lastDotIndex = filename.lastIndexOf('.');
    const ext = lastDotIndex !== -1 ? filename.slice(lastDotIndex).toLowerCase() : '';

    // 判断扩展名是否属于图片类型
    return imageExtensions.includes(ext) ? 'image' : 'file';
}

/**
 * 检查传入的值是否为对象组成的数组。
 *
 * @param list 要检查的值，可以是任意类型。
 * @return 如果该值是一个非空数组，且数组中的每一项都是非 null 的对象且不是数组，则返回 true。否则返回 false。
 */

export function devIsObjectArray(list: any): list is Record<string, any>[] {
    return (
        Array.isArray(list) &&
        list.length > 0 &&
        list.every(item => typeof item === 'object' && item !== null && !Array.isArray(item))
    );
}
